//	Ordinaire.Web.PaginationBuilder
//	Copyright (c) 2008 Jonathan Loe
//
//	MIT license (http://en.wikipedia.org/wiki/MIT_License)
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights 
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
//	of the Software, and to permit persons to whom the Software is furnished to do so, 
//	subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all 
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//	FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Ordinaire.Web
{
	/// <summary>
	/// PaginationBuilder is a tool that helps to build list of most-commonly used web controls
	/// used for pagination.
	/// </summary>
	public class PaginationBuilder
	{
		#region Variables declaration
		public const int DEFAULT_MAXIMUM_PAGES = 10;

		private static PaginationBuilder _instance;

		private int _maximumPages;
		private string _cssClass;
		private string _currentCssClass;
		private string _idPrefix = String.Empty;
		private string _separator;

		private Control _firstPageControl;
		private Control _previousPageControl;
		private Control _nextPageControl;
		private Control _lastPageControl;
		#endregion

		#region Constructor
		/// <summary>
		/// Creates new instance of Ordinaire.Web.PaginationBuilder class.
		/// </summary>
		private PaginationBuilder() : this(DEFAULT_MAXIMUM_PAGES) { }

		/// <summary>
		/// Creates new instance of Ordinaire.Web.PaginationBuilder class
		/// with the specified maximum number of pages to be displayed on
		/// one page.
		/// </summary>
		/// <param name="maximumPages">maximum number of pages</param>
		private PaginationBuilder(int maximumPages)
		{
			_maximumPages = maximumPages;
		}
		#endregion

		#region Public methods
		#region Singleton methods
		/// <summary>
		/// Creates new instance of Ordinaire.Web.PaginationBuilder class.
		/// </summary>
		public static PaginationBuilder Instance
		{
			get
			{
				if (_instance == null) { _instance = new PaginationBuilder(); }
				return _instance;
			}
		}

		/// <summary>
		/// Creates new instance of Ordinaire.Web.PaginationBuilder class
		/// with the specified maximum number of pages on a single page.
		/// </summary>
		/// <param name="maximumPages">maximum number of pages</param>
		/// <returns>new instance of Ordinaire.Web.PaginationBuilder class</returns>
		/// <exception cref="System.ArgumentException"></exception>
		public static PaginationBuilder CreateInstance(int maximumPages)
		{
			if (maximumPages < 1)
			{
				throw new ArgumentException(
					String.Format(ExceptionMessage.MoreThanOneFormat, "maximumPages")
				);
			}

			if (_instance == null) { _instance = new PaginationBuilder(maximumPages); }
			// resizes maximum number of pages if instance already exists.
			else { _instance.MaximumPages = maximumPages; }

			return _instance;
		}
		#endregion

		#region Property methods
		/// <summary>
		/// Gets and sets the maximum number of pages
		/// </summary>
		public int MaximumPages
		{
			get { return _maximumPages; }
			set
			{
				if (value < 1) { throw new ArgumentException("The maximum number of pages must be equal to or more than 1."); }
				_maximumPages = value;
			}
		}

		/// <summary>
		/// Gets and sets class name of the control of the other pages.
		/// </summary>
		public string CssClass
		{
			get { return _cssClass; }
			set { _cssClass = value; }
		}

		/// <summary>
		/// Gets and sets class name of the control of the current page.
		/// </summary>
		public string CurrentCssClass
		{
			get { return _currentCssClass; }
			set { _currentCssClass = value; }
		}

		/// <summary>
		/// Gets and sets the prefix of the id of the generated pagination controls.
		/// </summary>
		public string IdPrefix
		{
			get { return _idPrefix; }
			set { _idPrefix = value; }
		}

		/// <summary>
		/// Gets and sets the string separator between pages.
		/// </summary>
		public string Separator
		{
			get { return _separator; }
			set { _separator = value; }
		}

		/// <summary>
		/// Gets and sets the control of the first page.
		/// </summary>
		/// <remarks>Sets to null if you don't wish to have the control.</remarks>
		public Control FirstPageControl
		{
			get
			{
				if (_firstPageControl == null) { return null; }
				if (String.IsNullOrEmpty(_firstPageControl.ID)) { _firstPageControl.ID = _idPrefix + "First"; }
				return _firstPageControl;
			}
			set { _firstPageControl = value; }
		}

		/// <summary>
		/// Gets and sets the control of the previous page.
		/// </summary>
		/// <remarks>Sets to null if you don't wish to have the control.</remarks>
		public Control PreviousPageControl
		{
			get
			{
				if (_previousPageControl == null) { return null; }
				if (String.IsNullOrEmpty(_previousPageControl.ID)) _previousPageControl.ID = _idPrefix + "Previous";
				return _previousPageControl;
			}
			set { _previousPageControl = value; }
		}

		/// <summary>
		/// Gets and sets the control of the next page.
		/// </summary>
		/// <remarks>Sets to null if you don't wish to have the control.</remarks>
		public Control NextPageControl
		{
			get
			{
				if (_nextPageControl == null) { return null; }
				if (String.IsNullOrEmpty(_nextPageControl.ID)) _nextPageControl.ID = _idPrefix + "Next";
				return _nextPageControl;
			}
			set { _nextPageControl = value; }
		}

		/// <summary>
		/// Gets and sets the control of the last page.
		/// </summary>
		/// <remarks>Sets to null if you don't wish to have the control.</remarks>
		public Control LastPageControl
		{
			get
			{
				if (_lastPageControl == null) { return null; }
				if (String.IsNullOrEmpty(_lastPageControl.ID)) _lastPageControl.ID = _idPrefix + "Last";
				return _lastPageControl;
			}
			set { _lastPageControl = value; }
		}
		#endregion

		/// <summary>
		/// Builds pagination controls with the specified arguments set in the class properties.
		/// </summary>
		/// <param name="holder">the control that holds pagination controls</param>
		/// <param name="current">current page</param>
		/// <param name="total">total number of pages</param>
		/// <param name="handler">a EventHandler function to handle Click event of the controls.</param>
		public void BuildControls(Control holder, int current, int total, EventHandler handler)
		{
			Label lbl;
			LinkButton lb;

			if (current != 1)
			{
				if (FirstPageControl != null)
				{
					if (FirstPageControl is IButtonControl)
					{
						IButtonControl tmp = (IButtonControl)FirstPageControl;
						tmp.CommandArgument = 1.ToString();
						tmp.CausesValidation = false;
						if (handler != null) tmp.Click += handler;
					}
					holder.Controls.Add(FirstPageControl);

					holder.Controls.Add(new LiteralControl(HtmlEscapeCharacter.Space)); // insert space in between
				}

				if (PreviousPageControl != null)
				{
					if (PreviousPageControl is IButtonControl)
					{
						IButtonControl tmp = (IButtonControl)PreviousPageControl;
						tmp.CommandArgument = (current - 1).ToString();
						tmp.CausesValidation = false;
						if (handler != null) tmp.Click += handler;
					}
					holder.Controls.Add(PreviousPageControl);

					holder.Controls.Add(new LiteralControl(HtmlEscapeCharacter.Space));
				}
			}

			// Determine the start page
			int startPage = current - _maximumPages / 2;
			// Re-determine the start page if the ending page if greater than total page
			startPage = ((startPage + _maximumPages) > total) ? total - _maximumPages + 1 : startPage;
			// Set start page to first page if out of range (< 1)
			startPage = (startPage < 1) ? 1 : startPage;

			// Iterate all the links
			for (int i = 0; i < _maximumPages && (startPage + i) <= total; i++)
			{
				// Page number
				int tmp = startPage + i;

				// Add space at the beginning and end of the list or separator in between
				if (i != 0)
				{
					if (i == 1 && (tmp - 1) > 1) holder.Controls.Add(new LiteralControl(HtmlEscapeCharacter.Space));
					else if (i == (_maximumPages - 1) && tmp < total) holder.Controls.Add(new LiteralControl(HtmlEscapeCharacter.Space));
					else holder.Controls.Add(new LiteralControl(Separator));
				}

				// Create appropriate controls
				if (tmp == current) // Display label on current page
				{
					lbl = new Label();
					lbl.ID = _idPrefix + tmp.ToString();
					lbl.CssClass = CurrentCssClass;
					lbl.Text = tmp.ToString();
					lbl.EnableViewState = false;
					holder.Controls.Add(lbl);
				}
				else
				{
					lb = new LinkButton();
					lb.ID = _idPrefix + tmp.ToString();
					lb.CssClass = CssClass;
					lb.CommandArgument = tmp.ToString();

					if (i == 0) // First link
					{
						if (tmp != 1) lb.Text = "..."; // There's more before
						else lb.Text = tmp.ToString();
					}
					else if (i == _maximumPages - 1) // Last link
					{
						if (tmp != total) lb.Text = "..."; // There's more after
						else lb.Text = tmp.ToString();
					}
					else lb.Text = tmp.ToString();

					lb.CausesValidation = false;
					if (handler != null) lb.Click += handler;
					holder.Controls.Add(lb);
				}
			}

			if (current < total)
			{
				if (NextPageControl != null)
				{
					holder.Controls.Add(new LiteralControl(HtmlEscapeCharacter.Space));

					if (NextPageControl is IButtonControl)
					{
						IButtonControl tmp = (IButtonControl)NextPageControl;
						tmp.CommandArgument = (current + 1).ToString();
						tmp.CausesValidation = false;
						if (handler != null) tmp.Click += handler;
					}
					holder.Controls.Add(NextPageControl);
				}

				if (LastPageControl != null)
				{
					holder.Controls.Add(new LiteralControl(HtmlEscapeCharacter.Space));

					if (LastPageControl is IButtonControl)
					{
						IButtonControl tmp = (IButtonControl)LastPageControl;
						tmp.CommandArgument = total.ToString();
						tmp.CausesValidation = false;
						if (handler != null) tmp.Click += handler;
					}
					holder.Controls.Add(LastPageControl);
				}
			}
		}
		#endregion	}
	}
}
